/*
 * Copyright 2022 The Open Islands Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.openislands.oi.manager;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import org.openislands.oi.constant.RoleType;
import org.openislands.oi.constant.MessageCodeEnum;
import org.openislands.oi.constant.TaskState;
import org.openislands.oi.dao.entity.OfNodeTaskDO;
import org.openislands.oi.dao.mapper.OfNodeTaskMapper;
import org.openislands.oi.pojo.base.BeanBuilder;
import org.openislands.oi.pojo.dto.OfNodeTaskDTO;
import org.openislands.oi.scheduling.StateEngine;
import org.openislands.oi.util.IdUtils;
import org.openislands.oi.util.ValueCopyUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.Date;
import java.util.List;
import java.util.function.Consumer;

@Service
public class CommonOfNodeTaskManager {
    private static final Logger log = LoggerFactory.getLogger(CommonOfNodeTaskManager.class);

    private final GeneralManager<OfNodeTaskMapper, OfNodeTaskDO> ofNodeTaskManager;

    @Resource
    private StateEngine stateEngine;

    public CommonOfNodeTaskManager(OfNodeTaskMapper ofNodeTaskMapper) {
        this.ofNodeTaskManager = new GeneralManager<>(ofNodeTaskMapper);
    }

    public Boolean createOfNodeTask(OfNodeTaskDTO ofNodeTaskDTO) {
        OfNodeTaskDO ofNodeTaskDO = ValueCopyUtils.copyProperties(ofNodeTaskDTO, new OfNodeTaskDO());
        ofNodeTaskDO.setProgress(0);
        ofNodeTaskDO.setCreateTime(new Date());
        //todo: fill some fields, such as container type
        ofNodeTaskDO.setSessionId(IdUtils.getTaskSessionId(ofNodeTaskDTO));
        return ofNodeTaskManager.save(ofNodeTaskDO);
    }

    public List<OfNodeTaskDTO> queryOfNodeTask(String jobId, String taskName, Integer taskVersion, RoleType role, String nodeId) {
        return this.queryAnyOfNodeTask(BeanBuilder.create(new OfNodeTaskDTO())
                .set(OfNodeTaskDTO::setJobId, jobId)
                .set(OfNodeTaskDTO::setTaskName, taskName)
                .set(OfNodeTaskDTO::setTaskVersion, taskVersion)
                .set(OfNodeTaskDTO::setRole, role)
                .set(OfNodeTaskDTO::setNodeId, nodeId)
        );
    }

    public List<OfNodeTaskDTO> queryAnyOfNodeTask(OfNodeTaskDTO ofNodeTaskDTO) {
        return ofNodeTaskManager.queryAnyLikeDTO(ofNodeTaskDTO);
    }

    public List<OfNodeTaskDTO> queryAnyOfNodeTask(BeanBuilder<OfNodeTaskDTO> ofNodeTaskDTOBuilder) {
        return ofNodeTaskManager.queryAnyLikeDTO(ofNodeTaskDTOBuilder.build());
    }

    public List<OfNodeTaskDTO> queryConditionOfNodeTask(Consumer<LambdaQueryWrapper<OfNodeTaskDO>> conditions) {
        final List<OfNodeTaskDO> ofNodeTaskDOList = ofNodeTaskManager.queryAnyList(conditions);
        return ValueCopyUtils.copyList(ofNodeTaskDOList, ofNodeTaskDO -> ValueCopyUtils.copyProperties(ofNodeTaskDO, new OfNodeTaskDTO()));
    }

    public Boolean updateOfNodeTask(OfNodeTaskDTO ofNodeTaskDTO) {
        return this.updateAnyOfNodeTask(ofNodeTaskDTO,
                OfNodeTaskDO::getJobId, OfNodeTaskDO::getTaskName, OfNodeTaskDO::getTaskVersion, OfNodeTaskDO::getRole, OfNodeTaskDO::getNodeId);
    }

    public Boolean updateOfNodeTask(String jobId, String taskName, Integer taskVersion, RoleType role, String nodeId, OfNodeTaskDTO ofNodeTaskDTO) {
        ofNodeTaskDTO.setJobId(jobId);
        ofNodeTaskDTO.setTaskName(taskName);
        ofNodeTaskDTO.setTaskVersion(taskVersion);
        ofNodeTaskDTO.setRole(role);
        ofNodeTaskDTO.setNodeId(nodeId);
        return this.updateAnyOfNodeTask(ofNodeTaskDTO, OfNodeTaskDO::getJobId, OfNodeTaskDO::getTaskName, OfNodeTaskDO::getTaskVersion, OfNodeTaskDO::getRole, OfNodeTaskDO::getNodeId);
    }

    @SafeVarargs
    public final <Res> Boolean updateAnyOfNodeTask(OfNodeTaskDTO ofNodeTaskDTO, SFunction<OfNodeTaskDO, Res>... functions) {
        return ofNodeTaskManager.updateAnyLikeTask(ofNodeTaskDTO, OfNodeTaskDO::getState, ofNodeTaskDO -> ofNodeTaskDO.setState(null), functions);
    }

    public Boolean deleteOfNodeTaskByJob(String jobId) {
        return ofNodeTaskManager.delete(uw -> uw.eq(OfNodeTaskDO::getJobId, jobId));
    }

    public Boolean deleteOfNodeTask(String jobId, String taskName, Integer taskVersion) {
        return ofNodeTaskManager.delete(uw ->
                uw.eq(OfNodeTaskDO::getJobId, jobId).eq(OfNodeTaskDO::getTaskName, taskName).eq(OfNodeTaskDO::getTaskVersion, taskVersion));
    }

    public MessageCodeEnum updateState(String jobId, String taskName, Integer taskVersion, RoleType role, String nodeId, TaskState toState) {
        return stateEngine.updateState(ofNodeTaskManager.getBaseMapper(), toState, OfNodeTaskDO::getState, TaskState::name,
                lambdaWrapper -> lambdaWrapper.eq(OfNodeTaskDO::getJobId, jobId).eq(OfNodeTaskDO::getTaskName, taskName)
                        .eq(OfNodeTaskDO::getTaskVersion, taskVersion).eq(OfNodeTaskDO::getRole, role).eq(OfNodeTaskDO::getNodeId, nodeId)
        ).getCode();
    }
}
